#!/usr/bin/env python

# Copyright 2012 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Street Identity AP Client."""

import logging
import urllib
import httplib2
from oauth2client import client
from oauth2client.client import AccessTokenRefreshError
from openidconnect.client import  OpenIDConnectFlow
from streetidentity_ap.error import RegistrationError
from streetidentity_ap.registration import RegistrationResponse
from error import ValidationError
from validation import ValidationResponse

# TODO(mmachulak) move to a separate module or use anyjson from oauth2client
try:  # pragma: no cover
  import simplejson
except ImportError:  # pragma: no cover
  try:
    # Try to import from django, should work on App Engine
    from django.utils import simplejson
  except ImportError:
    # Should work for Python2.6 and higher.
    import json as simplejson

__author__ = 'Maciej Machulak'
__maintainer__ = 'Maciej Machulak'
__email__ = 'mmachulak@google.com'

__copyright__ = 'Copyright 2012, Google Inc.'
__license__ = 'Apache License 2.0'
__version__ = '0.2'
__status__ = 'Prototype'


class StreetIdentityAPFlow(OpenIDConnectFlow):
  """Street Identity Flow for Attribute Providers."""

  def __init__(self,
               scope='https://www.googleapis.com/auth/userinfo.profile '
               'https://www.googleapis.com/auth/userinfo.email '
               'https://www.googleapis.com/auth/streetidentity.write',
               attribute_uri='https://www.googleapis.com/oauth2/v1/attributes',
               token_verification_api=
               'https://www.googleapis.com/oauth2/v1/attribute_verification',
               **kwargs):

    super(StreetIdentityAPFlow, self).__init__(scope=scope, **kwargs)

    self.attribute_uri = attribute_uri
    self.token_verification_api = token_verification_api

  def register_attribute(self, attribute, credentials, http=None):
    """Register attribute at the IDP."""

    if http is None:
      http = httplib2.Http()

    http = credentials.authorize(http)

    try:
      resp, content = http.request(self.attribute_uri,
                                   method='POST',
                                   body=urllib.urlencode(
                                       {'type': attribute.get('type'),
                                        'desc': attribute.get('desc')}),
                                   headers={'content-type':
                                            'application/x-www-form-urlencoded',
                                            'accept-encoding': ''})
    except AccessTokenRefreshError:
      logging.error('could not refresh token')
      raise RegistrationError('could not refresh access token')

    if resp.status == 201:
      logging.debug(content)
      data = simplejson.loads(content)
      logging.debug(data)
      logging.debug(resp)
      registration_response = RegistrationResponse(data, resp)
      logging.debug(
          'Successfully registered attribute: %s', registration_response)
      return registration_response
    else:
      logging.error('Failed to register attribute: %s', content)
      error_msg = 'Invalid response %s.', resp['status']
      try:
        data = simplejson.loads(content)
        if 'error' in data:
          error_msg = data['error']
      except Exception:
        pass

      raise RegistrationError(error_msg)

  def remove_attribute(self, attribute_location, credentials, http=None):
    """Remove attribue from the IDP."""

    if http is None:
      http = httplib2.Http()

    http = credentials.authorize(http)
    try:
      resp, content = http.request(attribute_location, method='DELETE')
    except AccessTokenRefreshError:
      logging.error('could not refresh token')
      raise RegistrationError

    if resp.status == 204:
      logging.debug('Successfully removed the attribute from IDP')
      return True
    elif resp.status == 404:
      logging.debug(
          'Could not find attribute on IDP - OK to remove locally')
      return False
    else:
      logging.error('Failed to remove the attribute: %s', content)
      error_msg = 'Invalid response %s.', resp['status']
      try:
        data = simplejson.loads(content)
        if 'error' in data:
          error_msg = data['error']
      except Exception:
        pass

      raise RegistrationError(error_msg)

  def verify_token(self, token, audience, credentials, http=None, static=False,
                   cert_uri=client.ID_TOKEN_VERIFICATON_CERTS):
    """Validate attribute from the IDP."""

    # Try to validate token statically (does not work on GAE)
    if static:
      return client.verify_id_token(token, audience, http, cert_uri=cert_uri)

    # Try to validate token using IDP's Token Verification API
    if http is None:
      http = httplib2.Http()

    http = credentials.authorize(http)
    try:
      resp, content = http.request(self.token_verification_api,
                                   method='POST',
                                   body=urllib.urlencode(
                                       {'token': token}),
                                   headers={'content-type':
                                            'application/x-www-form-urlencoded',
                                            'accept-encoding': ''})
    except AccessTokenRefreshError:
      logging.error('could not refresh token')
      raise ValidationError('could not refresh access token')

    if resp.status == 200:
      data = simplejson.loads(content)
      validation_response = ValidationResponse(data)
      logging.debug(
          'Successfully validated token: %s', validation_response)
      return validation_response
    else:
      logging.error('Failed to validate token: %s', content)
      error_msg = 'Invalid response %s.', resp['status']
      try:
        data = simplejson.loads(content)
        if 'error' in data:
          error_msg = data['error']
      except Exception:
        pass

      raise ValidationError(error_msg)
